
#include <BFile>
#include <BSonDocument>
#include "member_BSonDocument.h"
#include "member_BSonNode.h"

using namespace BWE;

#define member					(*(member_BSonDocument*)_ptr)
#define member_allocate()		_ptr = new member_BSonDocument(this)
#define member_release()		delete (member_BSonDocument*)_ptr

BSonDocument::BSonDocument()
{
	member_allocate();
}
BSonDocument::~BSonDocument()
{
	for (int i = 0; i < member.nodes.size(); i++)
	{
		BSonNode* node = member.nodes[i];
		son_member(node)->document = 0;
	}
	member_release();
}

bool BSonDocument::setFormat(const BString& format)
{
	if (member.format.text != format)
	{
		member.format.fromString(format);
		return true;
	}
	return true;
}
const BString& BSonDocument::format() const
{
	return member.format.text;
}

BSonNode* BSonDocument::load(const BString& fileName)
{
	BFile file(fileName);
	if (file.open(IO_ReadText))
	{
		return read(file);
	}
	return 0;
}
bool BSonDocument::save(const BString& fileName, const BSonNode* root) const
{
	if (!root)
		return false;
	BFile file(fileName);
	if (file.open(IO_WriteText))
	{
		return write(file, root);
	}
	return false;
}
bool BSonDocument::save(const BString& fileName) const
{
	if (member.nodes.empty())
		return false;
	BFile file(fileName);
	if (file.open(IO_WriteText))
	{
		return write(file);
	}
	return false;
}

BSonNode* BSonDocument::read(const BString& sontext)
{
	member.last = 0;
	member.nodes.clear();
	BString text;
	char code = 0;
	int pos = 0;
	while (pos < sontext.size())
	{
		code = sontext[pos++];
		if (code == '\n')
			member.last = code;
		if (code == '\n' && pos > member.linePos)
		{
			member.lineNumber++;
			member.linePos = pos;
		}
		if (code == '\\')
		{
			text.append(sontext(pos++));
			continue;
		}
		if (code == '/' && text.endWith('/'))
		{
			BSonNode* child = new BSonNode("//");
			son_member(child)->document = this;
			member.readComment(child, sontext, pos);
			member.nodes.append(child);
			continue;
		}
		if (code == '{')
		{
			text.trimmed();
			BSonNode* node = new BSonNode(text);
			member.readNode(node, sontext, pos, 0);
			son_member(node)->format.head_prev = member.last;
			son_member(node)->document = this;
			member.nodes.append(node);
			text.reset();
			continue;
		}
		if (code == '}')
		{
			text.reset();
			continue;
		}
		text.append(code);
	}
	for (int i = 0; i < member.nodes.size(); i++)
	{
		member.makeFormat(member.nodes[i]);
	}
	return member.nodes.first();
}
bool BSonDocument::write(BString& sontext, const BSonNode* root) const
{
	if (!root)
		return false;
	member.writeNode(root, sontext, 0);
	return true;
}
bool BSonDocument::write(BString& sontext) const
{
	if (member.nodes.empty())
		return false;
	for (int i = 0; i < member.nodes.size(); i++)
	{
		member.writeNode(member.nodes[i], sontext, 0);
	}
	return true;
}

BSonNode* BSonDocument::read(const BStream& stream)
{
	member.last = 0;
	member.nodes.clear();
	BString text;
	char code = 0;
	while (stream.read(code))
	{
		if (code == '\n')
			member.last = code;
		if (code == '\n' && stream.offset() > member.linePos)
		{
			member.lineNumber++;
			member.linePos = stream.offset();
		}
		if (code == '\\')
		{
			stream.read(code);
			text.append(code);
			continue;
		}
		if (code == '/' && text.endWith('/'))
		{
			BSonNode* child = new BSonNode("//");
			son_member(child)->document = this;
			member.readComment(child, stream);
			text.reset();
			continue;
		}
		if (code == '{')
		{
			text.trimmed();
			BSonNode* node = new BSonNode(text);
			member.readNode(node, stream, 0);
			son_member(node)->format.head_prev = member.last;
			son_member(node)->document = this;
			member.nodes.append(node);
			text.reset();
			continue;
		}
		if (code == '}')
		{
			text.reset();
			continue;
		}
		text.append(code);
	}
	for (int i = 0; i < member.nodes.size(); i++)
	{
		member.makeFormat(member.nodes[i]);
	}
	return member.nodes.first();
}
bool BSonDocument::write(BStream& stream, const BSonNode* root) const
{
	if (!root)
		return false;
	member.writeNode(root, stream, 0);
	return true;
}
bool BSonDocument::write(BStream& stream) const
{
	if (member.nodes.empty())
		return false;
	for (int i = 0; i < member.nodes.size(); i++)
	{
		member.writeNode(member.nodes[i], stream, 0);
	}
	return true;
}

void BSonDocument::setNode(BSonNode* node)
{
	BHolder<BSonNode> holder(node);
	member.nodes.clear();
	member.nodes.append(node);
}
BSonNode* BSonDocument::node()
{
	return member.nodes.first();
}

void BSonDocument::addNode(BSonNode* node)
{
	if (node && son_member(node)->document != this)
	{
		BHolder<BSonNode> holder(node);
		if (son_member(node)->document)
			son_member(node)->document->removeNode(node);
		son_member(node)->document = this;
		member.nodes.append(node);
	}
}
BSonNode* BSonDocument::addNode(const BString& name)
{
	BSonNodeHolder node = new BSonNode(name);
	addNode(node);
	return node;
}

bool BSonDocument::removeNode(BSonNode* node)
{
	if (node && son_member(node)->document == this)
	{
		son_member(node)->document = 0;
		return member.nodes.remove(node);
	}
	return false;
}
bool BSonDocument::removeNode(const BString& name)
{
	if (BSonNode* node = this->node(name))
	{
		son_member(node)->document = 0;
		return member.nodes.remove(node);
	}
	return false;
}
void BSonDocument::clear()
{
	member.format.clear();
	for (int i = 0; i < member.nodes.size(); i++)
	{
		BSonNode* node = member.nodes[i];
		son_member(node)->document = 0;
	}
	member.nodes.clear();
}

int BSonDocument::nodeCount() const
{
	return member.nodes.size();
}
BSonNode* BSonDocument::node(int index)
{
	return member.nodes(index);
}
const BSonNode* BSonDocument::node(int index) const
{
	return member.nodes(index);
}

BSonNode* BSonDocument::node(const char* name)
{
	if (!name)
		return 0;
	for (int i = 0; i < member.nodes.size(); i++)
	{
		if (member.nodes[i]->name() == name)
			return member.nodes[i];
	}
	return 0;
}
const BSonNode* BSonDocument::node(const char* name) const
{
	if (!name)
		return 0;
	for (int i = 0; i < member.nodes.size(); i++)
	{
		if (member.nodes[i]->name() == name)
			return member.nodes[i];
	}
	return 0;
}

BSonNode* BSonDocument::node(const BString& name)
{
	if (name.empty())
		return 0;
	for (int i = 0; i < member.nodes.size(); i++)
	{
		if (member.nodes[i]->name() == name)
			return member.nodes[i];
	}
	return 0;
}
const BSonNode* BSonDocument::node(const BString& name) const
{
	if (name.empty())
		return 0;
	for (int i = 0; i < member.nodes.size(); i++)
	{
		if (member.nodes[i]->name() == name)
			return member.nodes[i];
	}
	return 0;
}

BSonNode* BSonDocument::create(const BString& path)
{
	int end = path.find('/');
	if (end == path.size())
	{
		BSonNode* node = this->node(path);
		if (node == 0)
			node = this->addNode(path);
		return node;
	}
	BString text;
	path.substr(text, 0, end);
	BSonNode* node = this->node(text);
	if (node == 0)
	{
		node = this->addNode(text);
	}
	int pos = end + 1;
	while (node && pos < path.size())
	{
		end = path.find('/', pos);
		path.substr(text, pos, end);
		BSonNode* child = node->child(text);
		if (child == 0)
			child = node->addChild(text);
		node = child;
		pos = end + 1;
	}
	return node;
}

BSonNode* BSonDocument::locate(const BString& path)
{
	int end = path.find('/');
	if (end == path.size())
		return this->node(path);
	BString text;
	path.substr(text, 0, end);
	BSonNode* node = this->node(text);
	int pos = end + 1;
	while (node && pos < path.size())
	{
		end = path.find('/', pos);
		path.substr(text, pos, end);
		node = node->child(text);
		pos = end + 1;
	}
	return node;
}
const BSonNode* BSonDocument::locate(const BString& path) const
{
	int end = path.find('/');
	if (end == path.size())
		return this->node(path);
	BString text;
	path.substr(text, 0, end);
	const BSonNode* node = this->node(text);
	int pos = end + 1;
	while (node && pos < path.size())
	{
		end = path.find('/', pos);
		path.substr(text, pos, end);
		node = node->child(text);
		pos = end + 1;
	}
	return node;
}

